package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"time"

	"gopkg.in/gomail.v2"
	"gopkg.in/yaml.v3"
)

type Config struct {
	Params Param  `yaml:"param"`
	Tasks  []Task `yaml:"tasks"`
}

type Param struct {
	Mail        Mail_config `yaml:"mail"`
	Period      int
	RetryPeriod int
	RetryTime   int
	Sleep       int
	Log         string
	Test        bool
}

type Mail_config struct {
	Smtp_server string
	Port        int
	User        string
	Pwd         string
	Nickname    string
}

type Task struct {
	Name      string
	Descri    string
	Rec_list  []string
	Cc_list   []string
	Path      string
	lastTime  int64          //最后一次执行时间
	Time_node []Timer_config `yaml:"timer_node"`
}

type Timer_config struct {
	Time    uint32
	Todtime uint32
	Descri  string
}

func Init() (err error) {
	confFileBytes, err := os.ReadFile("conf.yaml")
	if err != nil {
		return err
	}

	err = yaml.Unmarshal(confFileBytes, &config)
	if err != nil {
		return err
	}

	logFile, err := os.OpenFile(config.Params.Log, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)

	if err != nil {
		//fmt.Println("Open log file failed!!", err)
		return err
	}

	log.SetOutput(logFile)
	log.SetFlags(log.Ldate | log.Ltime)

	return nil
}

var config = Config{}

func main() {
	err := Init()
	if err != nil {
		fmt.Println("Init Failed!-->", err)
		os.Exit(-1)
	}
	log.Println("[Info]", "Mofs START........")

	log.Println("[Info]", "Smtp_server=", config.Params.Mail.Smtp_server)

	log.Println("[Info]", "Period=", config.Params.Period)
	log.Println("[Info]", "RetryTime=", config.Params.RetryTime)
	log.Println("[Info]", "RetryPeriod=", config.Params.RetryPeriod)

	for _, task := range config.Tasks {
		log.Println("[Info]", task.Name, "------>OK！")
	}

	for {
		t := time.Now().Unix()
		today0 := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.Local)
		ti := t - today0.Unix() //当前时间

		for i, task := range config.Tasks {
			for _, momen := range task.Time_node {
				if t-int64(task.lastTime) > 60 && int64(momen.Time) > ti-int64(config.Params.Period) && int64(momen.Time) < ti+int64(config.Params.Period) {

					files := []string{}

					filesInfo, err := ioutil.ReadDir(task.Path)
					if err != nil {
						log.Println("[Err]", task.Name, "------>获取附件失败！")
					} else {
						for _, file := range filesInfo {
							if !file.IsDir() {
								files = append(files, task.Path+"/"+file.Name())
							}
						}
					}
					if len(files) < 1 {
						log.Println("[Warring]", task.Name, momen.Descri, "------>未发现看板文件，邮件未发送 ！")
					} else {
						go SendGoMail(task, momen, files)
					}
					config.Tasks[i].lastTime = t
				}
			}
		}

		time.Sleep(time.Duration(config.Params.Sleep) * time.Second)
	}
}

/*
title 使用gomail发送邮件
@param []string mailAddress 收件人邮箱
@param string subject 邮件主题
@param string body 邮件内容

@param string attaches 附件内容
@return error
*/
func SendGoMail(task Task, momen Timer_config, attaches []string) {

	log.Println("[Info]", task.Name, momen.Descri, "------>邮件开始发出！")
	m := gomail.NewMessage()
	m.SetHeader("From", config.Params.Mail.Nickname+"<"+config.Params.Mail.User+">")

	// 发送给多个用户
	if len(task.Rec_list) < 1 {
		log.Println("[Err]", task.Name, momen.Descri, "------>没人收件人，邮件发出失败！")
		return
	}

	m.SetHeader("To", task.Rec_list...)

	if len(task.Cc_list) > 0 {
		m.SetHeader("Cc", task.Cc_list...)
	}

	// 设置邮件主题
	m.SetHeader("Subject", task.Name+momen.Descri)
	// 设置邮件正文
	m.SetBody("text/html", task.Name+momen.Descri+" 看板已发出，请及时处理！！")
	for i, file := range attaches {
		_, err := os.Stat(file)
		if err != nil {
			log.Println("[Err]", "附件", i+1, "------>>>>", file, "不存在!!")
		} else {
			log.Println("[Info]", "附件", i+1, "------>>>>", file, "上传成功!!")
			m.Attach(file)
		}
	}
	d := gomail.NewDialer(config.Params.Mail.Smtp_server, config.Params.Mail.Port, config.Params.Mail.User, config.Params.Mail.Pwd)

	// 发送邮件
	if err := d.DialAndSend(m); err != nil {
		log.Println("[Err]", task.Name, momen.Descri, "------>邮件发出失败！", err)
		var i = 0
		for i = 0; i < config.Params.RetryTime; i++ {
			log.Println("[Info]", task.Name, momen.Descri, "------>邮件重发第", i+1, "次！")
			if err := d.DialAndSend(m); err == nil {
				log.Println("[Info]", task.Name, momen.Descri, "------>邮件重发成功！")
				MoveFile(task, momen, attaches)
				break
			} else {
				log.Println("[Err]", task.Name, momen.Descri, "邮件重发第", i+1, "次------>失败！！", err)
			}
			time.Sleep(time.Duration(config.Params.RetryPeriod) * time.Second)
		}

		if i >= config.Params.RetryTime {
			log.Println("[Err]", task.Name, momen.Descri, "------>邮件重发失败！！")
		}

	} else {
		log.Println("[Info]", task.Name, momen.Descri, "------>邮件发出成功！")
		MoveFile(task, momen, attaches)
	}
}

/*
PathExists 判断一个文件或文件夹是否存在
输入文件路径，根据返回的bool值来判断文件或文件夹是否存在
*/
func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func MoveFile(task Task, momen Timer_config, files []string) {
	if res, _ := PathExists(task.Path + "/已发送"); !res {
		errr := os.MkdirAll(task.Path+"/已发送", 0777)
		if errr != nil {
			log.Println("[Err]", task.Name, momen.Descri, "------>创建目录失败[", task.Path+"/已发送]")
		}
	}

	for _, file := range files {
		if !config.Params.Test {
			path, name := filepath.Split(file)
			err := os.Rename(file, path+"/已发送/"+name)
			if err != nil {
				log.Println("[Err]", task.Name, momen.Descri, "------>移动文件失败[", file+"------>>>>/已发送]")
			}
		}
	}
}
